/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include <common/JsonHelper.h>
#include "common/utils/Utils.h"
#include "ApsaraStackResourceAccess.h"

namespace {
    const std::string MODULE_NAME = "ApsaraStackResourceAccess";
    const std::string APSARASTACK_API_CLASS = "ResourceApiWrapper";
    constexpr int32_t APS_CHCEK_USER_FAILED = 1577213587;
    const int32_t DEFAULT_TIMEOUT = 3000;
    const int32_t NOT_VERIFY = 0;
    const std::string SDK_SOURCE = "ghca";
    const std::string TYPE_REGION = "APS-region";
    const std::string TYPE_ZONE = "APS-zone";
    const std::string TYPE_RESOURCESET = "APS-resourceSet";
    const std::string TYPE_INSTANCE = "APS-instance";
    const std::string TYPE_DISK = "APS-disk";
    const std::string COMMON_FORMAT = "ssssssisss";
    const std::string FORMART_STRING = "s";
    const std::string FORMART_INT = "i";
    const std::string COMMON_RESOURCEGROUP = "RESOURCE_SET";
    const int LOCAL_REGION = 0;
    const int LOCAL_ZONE = 1;
    const int LOCAL_RESOURCEGROUP = 2;
    const int LOCAL_INSTANCE = 3;
    const int32_t ACTION_SUCCESS = 0;
    const int32_t ACTION_CONTINUE = 100;
    const int32_t ACTION_BUSY = 101;
    const int32_t ACTION_ERROR = 200;
    const std::string ERRO_INVALID_AK_SK = "IncompleteSignature";
}

namespace ApsaraStackPlugin {
    std::map<std::string, ResourceType> RESOURCE_MAP = {
        {TYPE_REGION, ResourceType::TYPE_REGION}, {TYPE_ZONE, ResourceType::TYPE_ZONE},
        {TYPE_RESOURCESET, ResourceType::TYPE_RESOURCESET}, {TYPE_INSTANCE, ResourceType::TYPE_INSTANCE},
        {TYPE_DISK, ResourceType::TYPE_DISK}
    };
ApsaraStackResourceAccess::ApsaraStackResourceAccess(ApplicationEnvironment appEnv) : m_appEnv(appEnv)
{}

ApsaraStackResourceAccess::ApsaraStackResourceAccess(ApplicationEnvironment appEnv, Application application)
    : m_appEnv(appEnv), m_application(application)
{}

ApsaraStackResourceAccess::ApsaraStackResourceAccess(ApplicationEnvironment appEnv, QueryByPage pageInfo)
    : m_appEnv(appEnv), m_condition(pageInfo)
{}

ApsaraStackResourceAccess::ApsaraStackResourceAccess(ApplicationEnvironment appEnv, Application application,
    QueryByPage pageInfo)
    : m_appEnv(appEnv), m_application(application), m_condition(pageInfo)
{}

ApsaraStackResourceAccess::~ApsaraStackResourceAccess()
{}

void ApsaraStackResourceAccess::SetApplication(Application application)
{
    DBGLOG("%s", application.id.c_str());
    m_application = application;
}

int32_t ApsaraStackResourceAccess::ListResource(ResourceResultByPage &page, const ListResourcePara &conditionPara)
{
    std::string response;
    if (GetResource(conditionPara, response) != SUCCESS) {
        ERRLOG("GetResource, erro response : %s, id: %s", response.c_str(), m_appEnv.id.c_str());
        return FAILED;
    }

    if (ParseResourceResponse(page, response, conditionPara) != SUCCESS) {
        ERRLOG("Failed to ParseResponse %s.", response.c_str());
        return FAILED;
    }
    DBGLOG("Get resource group list success.");
    return SUCCESS;
}

int32_t ApsaraStackResourceAccess::ParseResourceResponse(ResourceResultByPage &page,
    const std::string &response, const ListResourcePara &conditionPara)
{
    bool bRet = false;
    switch (RESOURCE_MAP[conditionPara.mResourceType]) {
        case ResourceType::TYPE_REGION:
            bRet = SetRegion(response, page);
            break;
        case ResourceType::TYPE_ZONE:
            bRet = SetZone(response, page, conditionPara);
            break;
        case ResourceType::TYPE_INSTANCE:
            bRet = SetInstance(response, page, conditionPara);
            break;
        case ResourceType::TYPE_DISK:
            bRet = SetDisk(response, page);
            break;
        case ResourceType::TYPE_RESOURCESET:
            bRet = SetResourceSet(response, page);
            break;
        default:
            ERRLOG("Not support resource type %s.", conditionPara.mResourceType.c_str());
            break;
    }
    return bRet ? SUCCESS : FAILED;
}

bool ApsaraStackResourceAccess::SetRegion(const std::string &response, ResourceResultByPage &page)
{
    RegionResponse resourceResponse;
    if (Utils::TransStrToApsaraApiResponse(response, resourceResponse) != SUCCESS) {
        ERRLOG("Failed to parse resource groups function result.");
        return false;
    }
    ApplicationResource returnValue;
    for (const auto &unit : resourceResponse.m_regions.m_region) {
        returnValue.__set_type("ApsaraStack");
        returnValue.__set_subType(TYPE_REGION);
        returnValue.__set_id(unit.m_regionId);
        returnValue.__set_name(unit.m_regionId);
        returnValue.__set_parentId(m_appEnv.id);
        returnValue.__set_parentName(m_appEnv.name);
        page.items.push_back(returnValue);
        INFOLOG("Get resource Region %s success.", unit.m_regionId.c_str());
    }
    page.__set_total(resourceResponse.m_regions.m_region.size());
    return true;
}

bool ApsaraStackResourceAccess::SetZone(const std::string &response, ResourceResultByPage &page,
    const ListResourcePara &conditionPara)
{
    ZoneResponse resourceResponse;
    if (Utils::TransStrToApsaraApiResponse(response, resourceResponse) != SUCCESS) {
        ERRLOG("Failed to parse resource groups function result.");
        return false;
    }
    ApplicationResource returnValue;
    for (const auto &unit : resourceResponse.m_zones.m_zone) {
        returnValue.__set_type("ApsaraStack");
        returnValue.__set_subType(TYPE_ZONE);
        returnValue.__set_id(unit.m_zoneId);
        returnValue.__set_name(unit.m_localName);
        returnValue.__set_parentId(conditionPara.mRegionId);
        returnValue.__set_parentName(conditionPara.mRegionName);
        page.items.push_back(returnValue);
        DBGLOG("Get resource zone %s success.", unit.m_localName.c_str());
    }
    page.__set_total(resourceResponse.m_zones.m_zone.size());
    return true;
}

bool ApsaraStackResourceAccess::SetInstance(const std::string &response, ResourceResultByPage &page,
    const ListResourcePara &conditionPara)
{
    InstanceResponse resourceResponse;
    if (Utils::TransStrToApsaraApiResponse(response, resourceResponse) != SUCCESS) {
        ERRLOG("Failed to parse create snapshot group function result.");
        return false;
    }
    ApplicationResource returnValue;
    Json::Value extendInfo;
    Json::FastWriter writer;
    for (const Instance &unit : resourceResponse.m_instances.m_instance) {
        returnValue.__set_type("ApsaraStack");
        returnValue.__set_subType(TYPE_INSTANCE);
        returnValue.__set_id(unit.m_id);
        returnValue.__set_name(unit.m_name);
        returnValue.__set_parentId(unit.m_zoneId);
        returnValue.__set_parentName(unit.m_zoneId);
        extendInfo["regionId"] = unit.m_regionId;
        extendInfo["regionName"] = conditionPara.mRegionName;
        extendInfo["zoneId"] = unit.m_zoneId;
        extendInfo["status"] = unit.m_status;
        extendInfo["resourceSetId"] = unit.mResourceGroup;
        extendInfo["resourceSetName"] = unit.m_resourceGroupName;
        extendInfo["os_type"] = unit.m_osType;
        std::vector<std::string> ipList;
        for (const auto &it : unit.m_networkInterfaces.m_networkInterface) {
            ipList.push_back(it.m_primaryIp);
        }
        extendInfo["vm_ip"] = boost::algorithm::join(ipList, ",");
        returnValue.__set_extendInfo(writer.write(extendInfo));
        page.items.push_back(returnValue);
        INFOLOG("Get resource instance %s success.", unit.m_name.c_str());
    }
    page.__set_total(resourceResponse.m_instances.m_instance.size());
    return true;
}

bool ApsaraStackResourceAccess::SetDisk(const std::string &response, ResourceResultByPage &page)
{
    DiskResponse resourceResponse;
    if (Utils::TransStrToApsaraApiResponse(response, resourceResponse) != SUCCESS) {
        ERRLOG("Failed to parse get disk function result.");
        return false;
    }
    ApplicationResource returnValue;
    Json::Value extendInfo;
    Json::FastWriter writer;
    for (const auto &unit : resourceResponse.m_disks.m_disk) {
        if (unit.m_category == "san_efficiency" || unit.m_category == "san_ssd") {
            --resourceResponse.m_total;
            continue;   // 过滤共享云盘
        }
        returnValue.__set_type("ApsaraStack");
        returnValue.__set_subType(TYPE_DISK);
        returnValue.__set_id(unit.m_id);
        returnValue.__set_name(unit.m_name);
        returnValue.__set_parentId(m_appEnv.id);
        returnValue.__set_parentName(m_appEnv.name);
        extendInfo["regionId"] = unit.m_regionId;
        extendInfo["zoneId"] = unit.m_zoneId;
        extendInfo["status"] = unit.m_status;
        extendInfo["instanceId"] = unit.m_instanceId;
        extendInfo["size"] = unit.m_size;
        extendInfo["storageSetId"] = unit.m_storageSetId;
        extendInfo["device"] = unit.m_device;
        extendInfo["type"] = unit.m_type;
        extendInfo["multiAttach"] = unit.m_multiAttach;
        extendInfo["imageId"] = unit.m_imageId;
        extendInfo["description"] = unit.m_description;
        extendInfo["category"] = unit.m_category;
        extendInfo["portable"] = unit.m_portable;
        returnValue.__set_extendInfo(writer.write(extendInfo));
        page.items.push_back(returnValue);
        DBGLOG("Get resource disk %s success.", unit.m_name.c_str());
    }
    page.__set_total(resourceResponse.m_disks.m_disk.size());
    return true;
}

bool ApsaraStackResourceAccess::SetResourceSet(const std::string &response, ResourceResultByPage &page)
{
    ResourceSetResponse resourceResponse;
    if (Utils::TransStrToApsaraApiResponse(response, resourceResponse) != SUCCESS) {
        ERRLOG("Failed to parse get resource groups function result.");
        return false;
    }
    ApplicationResource returnValue;
    Json::Value extendInfo;
    Json::FastWriter writer;
    for (const ResourceSet &unit : resourceResponse.mData) {
        returnValue.__set_type("ApsaraStack");
        returnValue.__set_subType(TYPE_RESOURCESET);
        returnValue.__set_id(std::to_string(unit.mId));
        returnValue.__set_name(unit.mResourceGroupName);
        returnValue.__set_parentId(m_appEnv.id);
        returnValue.__set_parentName(m_appEnv.name);
        extendInfo["organizationID"] = unit.mOrganizationId;
        extendInfo["organizationName"] = unit.mOrganizationName;
        extendInfo["resourceGroupName"] = unit.mResourceGroupName;
        extendInfo["id"] = unit.mId;
        returnValue.__set_extendInfo(writer.write(extendInfo));
        page.items.push_back(returnValue);
        INFOLOG("Get resourse set %s success.", unit.mResourceGroupName.c_str());
    }
    page.__set_total(resourceResponse.mData.size());
    return true;
}

int32_t ApsaraStackResourceAccess::CheckAppConnect(ActionResult& returnValue)
{
    std::string response = "";
    std::string instance = m_appEnv.name;
    PyObject* pArgs = nullptr; // connect_check方法不需要传参
    if (ExecutePython::GetInstance()->CallObjFunction(APSARASTACK_API_CLASS,
        "connect_check", pArgs, response) != SUCCESS) {
        ERRLOG("Failed to CallObjFunction connect_check.");
        if (response.find(ERRO_INVALID_AK_SK) != std::string::npos) {
            returnValue.__set_code(APS_CHCEK_USER_FAILED);
            returnValue.__set_bodyErr(APS_CHCEK_USER_FAILED);
            returnValue.__set_message("Accesskey ID or AccessKey Secret is invalid.");
        }
        return FAILED;
    }
    APSResponse checkBody;
    if (!Module::JsonHelper::JsonStringToStruct(response, checkBody)) {
        ERRLOG("Failed to parse resource groups function result.");
        return FAILED;
    }
    if (checkBody.m_statusCode != Module::SC_OK) {
        ERRLOG("Call api failed, erro : %d", checkBody.m_statusCode);
        returnValue.__set_bodyErr(APS_CHCEK_USER_FAILED);
        returnValue.__set_code(APS_CHCEK_USER_FAILED);
        returnValue.__set_message("Check vdc connect failed.");
        return FAILED;
    }
    returnValue.__set_code(ACTION_SUCCESS);
    DBGLOG("Check application connect success.");
    return SUCCESS;
}

int32_t ApsaraStackResourceAccess::GetResource(const ListResourcePara &conditionPara, std::string &response)
{
    PyObject* pArgs = nullptr;
    std::string functionName;
    std::string formart;
    INFOLOG("GetResource type: %s.", conditionPara.mResourceType.c_str());
    {
        std::lock_guard<std::mutex> guard(ExecutePython::m_pythonMutex);
        PyAllowThreads thread;
        switch (RESOURCE_MAP[conditionPara.mResourceType]) {
            case ResourceType::TYPE_REGION:
                functionName = "list_regions";
                break;
            case ResourceType::TYPE_ZONE:
                INFOLOG("GetResource TYPE_ZONE region: %s.", conditionPara.mRegionId.c_str());
                formart = FORMART_STRING;
                pArgs = Py_BuildValue(formart.c_str(), conditionPara.mRegionId.c_str());
                functionName = "list_zones";
                break;
            case ResourceType::TYPE_INSTANCE:
                formart = FORMART_STRING + FORMART_INT + FORMART_INT + FORMART_STRING + FORMART_STRING + FORMART_STRING;
                pArgs = Py_BuildValue(formart.c_str(), conditionPara.mRegionId.c_str(), m_condition.pageNo + 1,
                    m_condition.pageSize, "", conditionPara.mOrganizationId.c_str(),
                    conditionPara.mResourceGroupId.c_str());
                functionName = "list_instances";
                break;
            case ResourceType::TYPE_DISK:
                formart = FORMART_STRING + FORMART_STRING + FORMART_INT + FORMART_INT;
                pArgs = Py_BuildValue(formart.c_str(), conditionPara.mRegionId.c_str(),
                    conditionPara.mInstanceId.c_str(), m_condition.pageNo, m_condition.pageSize);
                functionName = "list_disks";
                break;
            case ResourceType::TYPE_RESOURCESET:
                formart = FORMART_STRING;

                pArgs = Py_BuildValue(formart.c_str(), conditionPara.mOrganizationId.c_str(), m_condition.pageNo + 1,
                    m_condition.pageSize);
                functionName = "list_resourcegroup_byorg";
                break;
            default:
                ERRLOG("Not support resource type %s.", conditionPara.mResourceType.c_str());
                break;
        }
    }
    if (ExecutePython::GetInstance()->CallObjFunction(APSARASTACK_API_CLASS, functionName, pArgs,
        response) != SUCCESS) {
        ERRLOG("Failed to CallObjFunction %s.", functionName.c_str());
        return FAILED;
    }
    INFOLOG("GetResource type: %s. success!", conditionPara.mResourceType.c_str());
    return SUCCESS;
}
}
